package cn.songdragon.zboschina.proxy;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.time.LocalDateTime;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.songdragon.zboschina.exception.NoProxyException;

public class ProxyManager {

	private static final Logger logger = LoggerFactory.getLogger(ProxyManager.class);

	private BlockingQueue<ProxyHost> proxies = new LinkedBlockingQueue<ProxyHost>();
	private Queue<ProxyHost> failedProxies = new LinkedBlockingQueue<ProxyHost>();
	private BlockingQueue<ProxyHost> waitingProxies = new LinkedBlockingQueue<ProxyHost>();
	private int loadedSize = 0;

	public void addProxy(String host, int port, String scheme) {
		ProxyHost proxy = new ProxyHost(host, port, scheme);
		if (!proxies.contains(proxy) && (checkAlive(host, port))) {
			logger.info("loadedSize=" + (++this.loadedSize));
			proxies.add(proxy);
		}
	}

	public void addProxy(String host, int port) {
		addProxy(host, port, HttpHost.DEFAULT_SCHEME_NAME);
	}

	public void loadProxyFromFileStream(InputStream inputStream) throws IOException {
		if (inputStream != null) {
			BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
			String line = null;
			while ((line = br.readLine()) != null) {
				if (line.contains(":")) {
					String[] proxyHost = line.split(":");
					int port = Integer.parseInt(proxyHost[1]);
					this.addProxy(proxyHost[0], port);

				}
			}
		}
	}

	public ProxyHost getAvaliableProxy() throws NoProxyException {
		ProxyHost proxy = null;
		try {
			proxy = proxies.poll(4, TimeUnit.SECONDS);
			if (proxy == null) {
				this.proxies.addAll(this.waitingProxies);
				this.waitingProxies.clear();
				proxy = proxies.poll(4, TimeUnit.SECONDS);
			}
			if (proxy != null && proxy.isAvaliable()) {
				return proxy;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return proxy;
	}

	public void returnProxy(ProxyHost proxy, RequestConfig request, boolean isSucc) {
		RequestRecord record = new RequestRecord();
		record.dateTime = LocalDateTime.now();
		record.request = request;
		if (isSucc) {
			proxy.increaseSuccCount();
		} else {
			proxy.increaseFailCount();
		}

		proxy.addRequestConfig(record);

		if (isSucc) {
			this.proxies.add(proxy);
		} else {
			if (proxy.getSuccCount() / 4 > proxy.getFailCount()) {
				this.waitingProxies.add(proxy);
			} else {
				this.failedProxies.add(proxy);
			}
		}
	}

	private boolean checkAlive(String host, int port) {
		boolean isReachable = false;
		Socket socket = null;
		try {
			socket = new Socket();

			// 端口号设置为 0 表示在本地挑选一个可用端口进行连接
			InetSocketAddress endpointSocketAddr = new InetSocketAddress(host, port);
			socket.connect(endpointSocketAddr, 2000);
			isReachable = true;
		} catch (IOException e) {
		} finally {
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e) {
				}
			}
		}
		return isReachable;
	}

	public int getAvaliableProxyCount() {
		return this.proxies.size();
	}
}
